package com.entrogy.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.entrogy.bean.StaticCourseEntity;
import com.entrogy.bean.StudentCourseScoreEntity;
import com.entrogy.service.ExamService;
import com.entrogy.service.StudentScoreService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 解析监听器，
 * 每解析一行会回调invoke()方法。
 * 整个excel解析结束会执行doAfterAllAnalysed()方法
 **/
public class StudentScoreExcelListener extends AnalysisEventListener<Map<Integer, String>> {

    private static final Logger logger = LoggerFactory.getLogger(StudentScoreExcelListener.class);

    private StudentScoreService studentScoreService;

    private ExamService examService;

    private static final int BATCH_COUNT = 5000;

    // 用户在前端点击的要导入的考试编号
    private Integer operationExamId;

    public StudentScoreExcelListener() {}

    public StudentScoreExcelListener(StudentScoreService studentScoreService, ExamService examService, Integer operationExamId) {
        this.studentScoreService = studentScoreService;
        this.examService = examService;
        this.operationExamId = operationExamId;
    }

    // excel中解析的考试编号
    private Integer examId;

    // 考试科目的id集合
    private List<Integer> examCourseIds = new ArrayList<>();

    // 存储成绩的表名
    private String scoreTableName;

    // 学生成绩详情集合
    private List<StudentCourseScoreEntity> datas = new ArrayList<>();

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        String firstCell = headMap.get(0);
        if ("考试编号".equals(firstCell)) {
            String s = headMap.get(1);
            // 通过excel的第一行取出考试编号
            this.examId = Integer.valueOf(s);
            logger.info("正在导入的考试id：{}", examId);
            if (!ObjectUtils.nullSafeEquals(examId, operationExamId)) {
                logger.error("导入成绩时失败，模板错误，解析到的examId：{}，操作的examId{}", examId, operationExamId);
                throw new RuntimeException("导入成绩时失败，模板错误，解析到的考试编号：" + examId + "，您操作的考试编号：" + operationExamId);
            }
            this.scoreTableName = examService.findScoreTableNameByExamId(this.examId);
            // 通过excel的第一行取出要考试的科目的列表，首先查出该次考试考了几科，根据考试的科目数量得到要截取哪些列作为考试科目id数组
            // excel前三列信息分别为学号，班级名称，学生姓名，所以取出后面的科目id长度
            headMap.forEach((k, v) -> {
                if (k > 2) {
                    this.examCourseIds.add(Integer.valueOf(v));
                }
            });
            // 校验模板正确性
            List<StaticCourseEntity> staticCourseList = studentScoreService.findExamStaticCourse(examId);
            for (StaticCourseEntity staticCourse : staticCourseList) {
                if (!examCourseIds.contains(staticCourse.getId())) {
                    logger.error("导入成绩时失败，模板错误，examId：{}，错误的课程id：{}，实际的课程id：{}", examId, staticCourse.getId(), staticCourseList);
                    throw new RuntimeException("导入成绩时失败，模板错误，科目编号被更改！");
                }
            }
        }
    }

    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        Integer studentId = data.get(0).contains(".") ? Integer.valueOf(data.get(0).substring(0, data.get(0).length() - 2)) : Integer.valueOf(data.get(0));
        List<Double> scoreInfo = new ArrayList<>();
        data.forEach((k, v) -> {
            if (k > 2) {
                scoreInfo.add(v == null ? Double.valueOf(0) : Double.valueOf(v));
            }
        });
        if (scoreInfo.size() != examCourseIds.size()) {
            for (int i = scoreInfo.size(); i < examCourseIds.size(); i++) {
                scoreInfo.add(Double.valueOf(0));
            }
        }
        for (int i = 0; i < scoreInfo.size(); i++) {
            StudentCourseScoreEntity studentCourseScoreEntity = new StudentCourseScoreEntity();
            Integer staticCourseId = examCourseIds.get(i);
            Double score = scoreInfo.get(i);
            // 根据静态课程id查询该次考试该生需要录入的成绩
            studentCourseScoreEntity
                    .setCourseId(staticCourseId)
                    .setExamId(this.examId)
                    .setScore(score)
                    .setStudentId(studentId);
            datas.add(studentCourseScoreEntity);
        }
        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (datas.size() >= BATCH_COUNT) {
            studentScoreService.batchUpdateScore(datas, this.scoreTableName);
            // 存储完成清理 list
            datas.clear();
        }
    }

    // 整个excel解析结束会执行doAfterAllAnalysed()方法
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        studentScoreService.batchUpdateScore(datas, this.scoreTableName);
        datas.clear();// 解析结束销毁不用的资源
        logger.info("所有数据解析完成！");
    }

    public Integer getExamId() {
        return examId;
    }

    public void setExamId(Integer examId) {
        this.examId = examId;
    }

}

